#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include "led_opr.h"


/*① 确定主设备号，也可以让内核分配*/
static int major = 0;
static struct class *led_class;

/* 定义一个结构指针，用来获取具体单板的返回值,具体在初始化函数里面调用get_board_led_opr() */
struct led_operations *p_led_opr ;


/*③ 实现对应的 drv_open/drv_read/drv_write 等函数，填入 file_operations 结构体*/
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t * offset)
{
	int err;
	int states; 
	struct inode *inode = file_inode(file);
	int minor = iminor(inode) ;
	err = copy_to_user(&states, buf, 1);
 	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    //printk("led_drv%d  %s",minor,states ? "ON" : "OFF");
	return 0;
}

/* write(fd,&val,1) */
ssize_t led_drv_write (struct file *file,const char __user *buf, size_t size, loff_t * offset)
{
	int err;
	char states;
	struct inode *inode = file_inode(file);
	int minor = iminor(inode) ;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(&states, buf, 1);

	/* 获取应用程序传过来的次设备号和LED的状态值控制指定的LED */
	p_led_opr->ctl(minor,states);
	
	return 1;
}

static int led_drv_open (struct inode *node, struct file *file )
{
	int minor = iminor(node); 
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	/* open函数在这里可以用来初始化LED，怎么做呢？根据次设备号来初始化LED */
	p_led_opr->init(minor);
	
 	return 0;
}

static int led_drv_close (struct inode *node, struct file *file )
{
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


/*② 定义自己的 file_operations 结构体*/
static  struct  file_operations led_drv={
	.owner     = THIS_MODULE,
	.read      = led_drv_read,
	.write     = led_drv_write,
	.open      = led_drv_open,
	.release   = led_drv_close,
	
};


/*④ 把 file_operations 结构体告诉内核： register_chrdev*/
/*⑤ 谁来注册驱动程序啊？得有一个入口函数：安装驱动程序时，就会去调用这个入口函数*/
static int __init led_init (void)
{
	int err;
	int i;

	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	major = register_chrdev(0, "my_led",& led_drv);//结构体告诉内核
	led_class = class_create(THIS_MODULE, "my_led");
	err = PTR_ERR(led_class);
	if (IS_ERR(led_class))
		return -1;
	/* 这个指针很重要，获取具体单板传过来的参数，主要是主设备号和LED数量 */
	p_led_opr = get_board_led_opr();
	for(i = 0 ; i < p_led_opr->num ; i++)
	{
		device_create(led_class, NULL, MKDEV(major, i), NULL, "my_led%d", i );//注册驱动程序 dev/my_led0,1,...

	}

	
	
	return 0;
}


/*⑥ 有入口函数就应该有出口函数：卸载驱动程序时，出口函数调用 unregister_chrdev*/
static void __exit led_exit(void)
{
	int i;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	for(i = 0 ; i < p_led_opr->num ; i++)
		device_destroy(led_class, MKDEV(major, i));
	class_destroy(led_class);
	unregister_chrdev(major, "my_led");	

}


/*⑦ 其他完善：提供设备信息，自动创建设备节点： class_create, device_create*/
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");







